define(['axios', 'base64', 'utils', 'dexie'], function (axios, base64, utils, Dexie) {

    const dbName = 'gitee';
    const db = new Dexie(dbName);

    const fileTableName = 'gitFiles';

    db.version(1).stores({
        [fileTableName]: "&[repo+path],sha,type,content,name,hasLoad,hasMdf,localContent"
    });

    // 文件表
    const gitTable = db[fileTableName];

    console.log(`IndexedDB初始化完成！`, db);

    const pubRepo = "webdata";//公开仓库
    const meRepo = "webme";//隐私仓库

    const storageKey = {
        lsRepo: "gitee-repo",
        lsLoginState: "wyd-login-state",//登录状态
        lsFileTree: "file-Tree",//文件树
    };

    let lsRepo = localStorage.getItem(storageKey.lsRepo);
    if (!lsRepo) localStorage.setItem(storageKey.lsRepo, pubRepo);

    let apiConfig = {
        client_id: "31d911f47eda3cad2d7e0b6923cbe4082d633cc81e79826804d574d850c9972a",
        client_secret: "a454f37510ca5483d4f004f02d016405a0a70e819d3ef14c8db1bf980ebfc39f",
        reposUrlPrefix: `https://gitee.com/api/v5/repos/yanhui2018`,
        repo: lsRepo ? lsRepo : pubRepo,
    };

    let state = {
        // 登录状态
        loginState: {},
        // 文件路径-sha
        fileShaMap: new Map()
    }

    let access_token;

    /**
     * 获取文件树
     * @param refresh 是否刷新
     * @param repo
     * @returns {Promise<unknown>}
     */
    async function getFileTree(refresh = false, repo = pubRepo) {
        let data = await gitTable.filter(file => file.repo === repo).toArray();
        if (data.length > 0 && !refresh) {
            console.log('fileTree', data);
            return data;
        }
        // 获取项目下的文件树
        let dbData = await axios.get(`${apiConfig.reposUrlPrefix}/${repo}/git/trees/master?${access_token ? 'access_token=' + access_token : ''}&recursive=1`);

        if (data.length > 0) {
            for (let cacheData of data) {
                let path = cacheData.path;
                let serverExist = false;
                for (let serverData of dbData.tree) {
                    if (serverData.path === path) {
                        serverExist = true;
                        break
                    };
                }
                if (!serverExist) await delLocalData(path, repo);
            }
        }
        // await gitTable.where("repo").equals(repo).delete();

        await saveFileToLocal(dbData.tree, repo);
        return dbData.tree;
    }

    /**
     * 获取文件内容
     * @param filePath 文件路径
     * @param refresh 是否刷新
     * @param parseJson 是否转json
     * @param repo 仓库
     * @returns {Promise<null|*>}
     */
    async function getFileContent(filePath, refresh = false, parseJson = false, repo = pubRepo) {
        let cacheData = await searchByKey(filePath, repo);
        if (!refresh && cacheData && cacheData.hasLoad) {
            let localContent = cacheData.localContent;
            if (parseJson) {
                try {
                    return JSON.parse(localContent);
                } catch (e) {
                    return {};
                }
            } else {
                return localContent;
            }
        }
        // repo !== pubRepo && 
        let data = await axios.get(`${apiConfig.reposUrlPrefix}/${repo}/contents/${filePath}${access_token ? '?access_token=' + access_token : ''}`);
        if (!data.content) {
            utils.message(`获取文件内容失败! [${filePath}]`, "error");
            return null;
        }
        let content = base64.decode(data.content);
        data.content = content;
        data.localContent = content;
        await saveFileToLocal(data, repo);
        if (parseJson) {
            return JSON.parse(content);
        }
        return content;
    }

    /**
     * 新增文件
     * @param filePath 文件路径
     * @param content 文件内容
     * @param repo
     * @returns {Promise<unknown>}
     */
    async function newFile(filePath, content, repo = pubRepo) {
        if (!filePath || !content) {
            return null;
        }
        let data = base64.encode(content);
        return new Promise((resolve) => {
            axios.post(`${apiConfig.reposUrlPrefix}/${repo}/contents/${filePath}`, {
                access_token,
                content: data,
                message: `open api new ${window.location.pathname}`
            }).then(async (responseData) => {
                console.log('新增成功！res=', responseData);
                utils.notify("新增成功！", "success");
                await getFileTree(true, repo);
                resolve(responseData);
            }).catch((err) => {
                console.error(err);
                utils.notify("新增异常！", "error");
            })
        })

    }

    /**
     * 更新文件
     * @param filePath 文件路径
     * @param content 文件内容
     * @param repo
     * @returns {Promise<void>}
     */
    async function updateFile(filePath, content, repo = pubRepo, refreshFileTree = true) {
        let cacheData = await searchByKey(filePath, repo);
        let localContent = cacheData.localContent ? cacheData.localContent : cacheData.content;
        let data = base64.encode(localContent);
        return new Promise((resolve) => {
            axios.put(`${apiConfig.reposUrlPrefix}/${repo}/contents/${filePath}`, {
                access_token,
                content: data,
                sha: cacheData.sha,
                message: `open api update ${window.location.pathname}`
            }).then(async (responseData) => {
                console.log('修改成功！res=', responseData);
                utils.notify(`上传[${filePath}]成功！`, "success");

                if (responseData.content) {
                    let { sha, name } = responseData.content;
                    gitTable.update({ repo, path: filePath }, { content: localContent, name, sha, hasLoad: false, hasMdf: false });
                } else {
                    await getFileTree(true, repo);
                    gitTable.update({ repo, path: filePath }, {
                        content: localContent
                    });
                }
                resolve();
            }).catch((err) => {
                console.error(err);
                utils.notify("上传异常！", "error");
            })
        })
    }

    /**
     * 更新文件本地缓存
     * @param filePath
     * @param content
     * @param repo
     * @returns {null}
     */
    async function updateFileCache(filePath, content, repo = pubRepo) {
        await gitTable.update({
            repo,
            path: filePath
        }, {
            localContent: content,
            hasMdf: true
        });
        utils.notify(`更新[${filePath}]成功！`, "success");
    }

    /**
     * 删除文件
     * @param filePath 文件路径
     * @param repo 仓库
     * @returns {Promise<unknown>}
     */
    async function deleteFile(filePath, repo = pubRepo) {
        let cacheData = await searchByKey(filePath, repo);
        return new Promise((resolve) => {
            axios.delete(`${apiConfig.reposUrlPrefix}/${repo}/contents/${filePath}`, {
                params: {
                    access_token,
                    sha: cacheData.sha,
                    message: `open api delete ${window.location.pathname}`
                }
            }).then(async () => {
                utils.notify(`删除[${filePath}]成功！`, "success");
                delLocalData(filePath, repo);
                resolve();
            }).catch((err) => {
                console.error(err);
                utils.notify("删除异常！", "error");
            })
        })

    }

    /**
     * 保存数据到本地
     * @param key
     * @param data
     * @param repo
     */
    async function saveFileToLocal(data, repo = pubRepo) {
        if (Array.isArray(data)) {
            // 加载文件树
            for (let file of data) {
                let existFile = await gitTable.where("[repo+path]").equals([repo, file.path]).first();
                if (!existFile) {
                    gitTable.put({ repo, ...file, content: "", localContent: "", name: "", hasLoad: false, hasMdf: false });
                } else {
                    if (existFile.sha !== file.sha) {
                        // 文档内容发生改变
                        gitTable.update({ repo, path: file.path }, { sha: file.sha, hasLoad: false, hasMdf: false });
                    }
                }
            }

        } else {
            // 加载单个文件后保存本地
            gitTable.put({ repo, ...data, hasLoad: true, hasMdf: false });
        }
    }



    /**
     * 获取本地数据
     * @param key
     * @param isObject
     * @param repo
     * @returns {string|null}
     */
    async function searchByKey(path, repo = pubRepo) {
        let data = await gitTable.where("[repo+path]").equals([repo, path]).first();
        return data ? data : {};
    }

    /**
     * 删除本地数据
     * @param key
     * @param repo
     */
    function delLocalData(filePath, repo = pubRepo) {
        gitTable.where("[repo+path]").equals([repo, filePath]).delete();
    }

    /**
     * 初始化本地localStorage
     */
    async function initState() {
        await refreshToken();
        loginStateInit();
        // 提前初始化配置
        await getFileTree(false, apiConfig.repo);
    }

    // 登录状态
    function loginStateInit() {
        let loginStorageData = localStorage.getItem(storageKey.lsLoginState);
        if (!loginStorageData) {
            // goLogin();
            console.warn("未登陆!");
            return;
        }
        state.loginState = JSON.parse(loginStorageData);
        access_token = state.loginState['access_token'];
    }

    async function clearAllCache() {
        for (let key in localStorage) {
            if (localStorage.hasOwnProperty(key)) {
                if (key && !key.startsWith("wyd-")) {
                    localStorage.removeItem(key);
                }
            }
        }
        await gitTable.limit(1000).delete();
    }

    /**
     * 设置指定仓库
     * @param repo
     */
    function setRepo(repo) {
        apiConfig.repo = repo;
        localStorage.setItem(storageKey.lsRepo, repo);
        utils.message(`切换数据源[${repo}]成功！`);
    }

    function getRepo() {
        return apiConfig.repo;
    }

    async function refreshToken() {
        let loginStorageData = localStorage.getItem(storageKey.lsLoginState);
        if (loginStorageData) {
            let loginState = JSON.parse(loginStorageData);
            let { created_at, expires_in, refresh_token } = loginState;
            if (Math.floor(Date.now() / 1000) - created_at > expires_in) {
                let data = await axios.post(`https://gitee.com/oauth/token?grant_type=refresh_token&refresh_token=${refresh_token}`);
                loginState = JSON.stringify(data);
                localStorage.setItem(storageKey.lsLoginState, loginState);
            }
            return loginState;
        }
        return {};
    }

    return {
        getFileTree,
        getFileContent,
        newFile,
        updateFile,
        deleteFile,
        initState,
        clearAllCache,
        setRepo,
        getRepo,
        storageKey,
        apiConfig,
        updateFileCache,
        refreshToken,
        loginStateInit,
        searchByKey,
        gitTable
    }

});
